package org.erikaredmark.monkeyshines; import java.util.Collection; import java.util.logging.Logger; /** * * Used for statistics and point calculations. Generated after a world is finished. * * @author Erika Redmark * */ public final class WorldStatistics { private static final String CLASS_NAME = "org.erikaredmark.monkeyshines.WorldStatistics"; private static final Logger LOGGER = Logger.getLogger(CLASS_NAME); /** * * Constructs the statistics object with the given raw data. * * @param allGoodies * a collection of all the goodies in the world. All non-essential goodies will be totaled and compared with * the number of collected goodies for a percentage. Only goodies that grant a non-zero score are under * consideration * * @param collectedGoodies * the number of goodies collected in the world (tally is based on goodies granting non-zero score) * * @param score * the raw score at the end of the game. This score will be modified based on other calculations for an eventual * total * * @param bonusTimer * countdown timer state before game end * * @param playtesting * if the game was played in playtesting mode or not. Scoring is inverted (negatives) in playtesting. * */ WorldStatistics(final Collection<Goodie> allGoodies, final int collectedGoodies, final int score, final int bonusTimer, final boolean playtesting) { int totalFruit = 0; for (Goodie g : allGoodies) { if (g.getGoodieType().score > 0) ++totalFruit; } // Note: Whilst the names are different, the algorithms for calculating the score are mostly identical to the original game // as these calculations were taken directly from the original source code. Small deviations were made in only a few // cases. // Fruit Bonus // Calculate percentage without conversion to double. This calculates how much fruit were REMAINING, not TAKEN. int fruitPercent = (100 * (totalFruit - collectedGoodies) ) / (totalFruit); // Change into percent taken. fruitPercent = 100 - fruitPercent; boolean badCalculation = false; if (fruitPercent > 100) { LOGGER.warning(CLASS_NAME + ": Possible calculation errors: ended up with above 100% fruit collection " + fruitPercent + "%. Further calculations will be handled assuming 100% collection. Outputting raw stat input: "); badCalculation = true; fruitPercent = 100; } else if (fruitPercent < 0) { LOGGER.warning(CLASS_NAME + ": Possible calculation errors: ended up with negative % fruit collection " + fruitPercent + "%. Further calculations will be handled assuming 0% collection. Outputting raw stat input: "); badCalculation = true; fruitPercent = 0; } if (badCalculation) { LOGGER.warning("Total Fruit: " + totalFruit); LOGGER.warning("Collected Fruit: " + collectedGoodies); LOGGER.warning("Raw Score: " + score); LOGGER.warning("Bonus Timer: " + bonusTimer); } fruitCollectedPercent = fruitPercent; if (fruitCollectedPercent == 100) this.fruitBonus = 10000; else if (fruitCollectedPercent > 95) this.fruitBonus = 7500; else if (fruitCollectedPercent > 90) this.fruitBonus = 5000; else if (fruitCollectedPercent > 80) this.fruitBonus = 2500; else if (fruitCollectedPercent > 50) this.fruitBonus = 1000; else this.fruitBonus = 0; // Time Bonus // Must be multiple of ten. It probably is unless the last red key was exactly where the exit door // would appear thus making the level end before the counter even ticked down. To handle that // case, 9999 is promoted to 10000. All other values are truncated to the nearest ten multiple. int tempBonus; if (bonusTimer == 9999) { tempBonus = 10000; } else { tempBonus = bonusTimer - (bonusTimer % 10); } timeBonus = tempBonus; // Raw Score rawScore = score; // Total!!! int tempTotalScore = rawScore + fruitBonus + timeBonus; // Wait, are we in playtesting mode? The best solution to a problem is usually the easiest one. if (playtesting) tempTotalScore = -tempTotalScore; totalScore = tempTotalScore; } public int getFuritCollectedPercent() { return fruitCollectedPercent; } public int getFruitBonus() { return fruitBonus; } public int getTimeBonus() { return timeBonus; } public int getRawScore() { return rawScore; } public int getTotalScore() { return totalScore; } private final int fruitCollectedPercent; private final int fruitBonus; private final int timeBonus; // Raw score is from before all modifiers are applied private final int rawScore; // Total is after all modification private final int totalScore; }